﻿using System;
using System.Drawing;
using System.Linq;
using MoreLinq;
using Redmine.Laptop.API;
using Redmine.Laptop.Common.Core;
using Redmine.Laptop.Common.Core.Enums;
using Redmine.Laptop.Reports.ReportParameters;

namespace Redmine.Laptop.Reports.Reports
{
    public class EmploymentRatesReport : Report
    {
        private readonly EmploymentRatesReportParameters _parameters;

        private const uint topRow = 2;
        private const uint leftColumn = 2;
        private uint bottomRow;
        private uint rightColumn;

        public EmploymentRatesReport(RedmineService redmineService, ReportType type, Parameters parameters)
            :base(redmineService, type, parameters)
        {
            _parameters = (EmploymentRatesReportParameters)parameters;
        }
        
        public override string ReportName
        {
            get { return string.Format("Employment Rates report ({0}-{1})", _parameters.FromDate.ToString("dd.MM.yyyy"), _parameters.ThruDate.ToString("dd.MM.yyyy")); }
        }

        protected override void CreateReportStructure()
        {
            DrawTableLayout();
            FillProjects();
            FillDevelopersInfo();
        }

        private void DrawTableLayout()
        {
            var projectsCount = ProjectsInfo.Count();
            var developersCount = TimeEntries.Select(te => te.User.UserName).Distinct().Count();

            bottomRow = (uint)(topRow + projectsCount + 1);
            rightColumn = (uint)(leftColumn + developersCount + 1);
            
            // draw internal table borders
            Document.SetBorder(topRow, leftColumn, bottomRow, rightColumn, Color.Black, BorderWeight.Hairline, LineStyle.Dot);

            // draw line between developers
            for (var i = leftColumn; i <= rightColumn; i++)
                Document.SetBorder(topRow, i, bottomRow, i, Color.Black, isDrawAllBorders: false, isDrawRightEdge: true);
            
            // set text align for developers information
            //Document.SetTextAlligment(topRow, leftColumn + 1, topRow, rightColumn - 1);

            // set styles for projects column 
            Document.SetBorder(topRow, leftColumn, bottomRow, leftColumn, Color.Black);
           
            // fill footer labels, draw footer background
            Document[bottomRow, leftColumn] = @"Итого часов: ";

            Document.SetBorder((uint)(topRow + projectsCount + 1), leftColumn, (uint)(topRow + projectsCount + 1), rightColumn, Color.Black);
            Document.SetBackground(CustomColor.LightGreen, (uint)(topRow + projectsCount + 1), leftColumn, (uint)(topRow + projectsCount + 1), rightColumn - 1);
            Document.SetBackground(Color.Red, (uint)(topRow + projectsCount + 1), rightColumn, (uint)(topRow + projectsCount + 1), rightColumn);
            
            // draw header
            Document[topRow, leftColumn] = @"Проект \ Разработчик";
            Document[topRow, rightColumn] = @"Итого часов: ";
            
            Document.SetBackground(CustomColor.LightGreen, topRow, leftColumn, topRow, rightColumn);
            Document.SetBorder(topRow, leftColumn, topRow, rightColumn, Color.Black);
            Document.SetFontSize(14, topRow, leftColumn, topRow, leftColumn);

            // fill summary column
            Document.SetBorder(topRow, rightColumn, bottomRow, rightColumn, Color.Black);
            Document.SetBackground(CustomColor.LightGreen, topRow, rightColumn, (uint)(topRow + projectsCount + 1), rightColumn);

            // set bold font for all cells
            Document.SetBoldFont(topRow, leftColumn, bottomRow, rightColumn);

            // fill background for non commercial projects
            foreach (var project in ProjectsInfo.Where(p => p.IsNonCommercial))
                Document.SetBackground(CustomColor.LightGray, (uint)(topRow + project.Index + 1), leftColumn, (uint)(topRow + project.Index + 1), rightColumn);
        }

        private void FillProjects()
        {
            var projects =
                TimeEntries.Select(te => te.Project.ProjectName)
                .Distinct()
                .OrderBy(p => p)
                .Select(p => new { ProjectName = p, IsNonCommercial = NonCommercialProjectsNames.Contains(p) });

            // fill projects
            uint rowIndex = 3;
            foreach (var project in projects)
                Document[rowIndex++, 2] = project.ProjectName;
        }

        private void FillDevelopersInfo()
        {
            var developerProjectHours =
                (
                    from te in TimeEntries
                    group te by new { te.User.UserName, te.Project.ProjectName } into grouped
                    let names = grouped.Key.UserName.Split(' ')
                    select new
                    {
                        grouped.Key.UserName,
                        DisplayName = string.Format("{0} {1}.", names[1], names[0][0]),
                        grouped.Key.ProjectName,
                        Hours = grouped.Sum(g => g.Hours),
                    } into dph
                    orderby dph.DisplayName, dph.ProjectName
                    select dph
                ).ToList();

            // fill developers hours info
            uint columnIndex = leftColumn + 1;
            foreach (var developer in developerProjectHours.DistinctBy(dph => dph.UserName))
            {
                uint rowIndex = topRow;

                // fill developer name
                Document[rowIndex, columnIndex] = developer.DisplayName;
                Document.SetTextAlignment(CellAlignment.Center, rowIndex++, columnIndex);
                
                // fill employment rates
                var developerProjects = developerProjectHours.Where(dph => dph.UserName == developer.UserName).ToList();
                foreach (var project in ProjectsInfo)
                {
                    var hours = developerProjects.Where(dph => dph.ProjectName == project.ProjectName).Sum(p => p.Hours);
                    var projectPercent = (hours * 100) / project.Hours;
                    var sumDeveloperHours = developerProjects.Sum(dph => dph.Hours);
                    var developerPercent = sumDeveloperHours != 0 ? (hours*100)/sumDeveloperHours : 0;

                    var hoursStr = string.Format("Ч: {0}; ", string.Format("{0:0.##}",hours));
                    var projectPercentStr = string.Format("П%: {0}; ", Math.Round(projectPercent, 2));
                    var developerPercentStr = string.Format("Р%: {0}; ", Math.Round(developerPercent, 2));

                    Document[rowIndex, columnIndex] = hoursStr + projectPercentStr + developerPercentStr;
                    Document.SetFontColor(CustomColor.DarkRed, rowIndex, columnIndex, rowIndex, columnIndex, 0, (uint)hoursStr.Length);
                    Document.SetFontColor(Color.DarkGreen, rowIndex, columnIndex, rowIndex, columnIndex, (uint)hoursStr.Length, (uint)projectPercentStr.Length);
                    Document.SetFontColor(CustomColor.DarkOrange, rowIndex, columnIndex, rowIndex, columnIndex, (uint)hoursStr.Length + (uint)projectPercentStr.Length, (uint)developerPercentStr.Length);
                    Document.SetTextAlignment(CellAlignment.Center, rowIndex, columnIndex);
                    rowIndex++;
                }

                // fill summary of developers hours
                var totalWorkingHours = developerProjects.Sum(dph => dph.Hours);
                Document[rowIndex++, columnIndex] = totalWorkingHours;

                columnIndex++;
            }

            // fill summary of projects hours info
            var row = topRow + 1;
            foreach (var project in ProjectsInfo)
                Document[row++, rightColumn] = project.Hours;

            // fill summary of working hours
            var summaryWorkingHours = developerProjectHours.Sum(dph => dph.Hours);
            Document[bottomRow, rightColumn] = summaryWorkingHours;
        }
    }
}
